package main

import (
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/gorilla/websocket"
)

func main() {
	http.HandleFunc("/ws", handleWebSocket)
	log.Fatal(http.ListenAndServe(":9090", nil)) // ListenAndServe 一直监听没有返回除非出错
}

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
	// websocket是基于HTTP协议的，因此它对HTTP有个Upgrader(升级程序)的动作。
	// 它提供了一些参数把HTTP的连接升级为WebSocket连接的参数。
	// 而且并发调用Upgrader的方法是安全的。
	u := &websocket.Upgrader{
		// 小程序真实的场景Origin是有很多外面的请求连过来，因此同源的问题只能放过。
		// 从协议本身很难判断是从小程序过来的请求，因此同源这里就放过了。
		// 但还是有别的办法去校验的，比如是否有登录(携带着请求头)。
		CheckOrigin: func(r *http.Request) bool {
			return true
		},
	}
	c, err := u.Upgrade(w, r, nil)
	if err != nil {
		fmt.Printf("cannot upgrade: %v\n", err)
		return
	}
	defer c.Close()

	done := make(chan struct{})
	go func() {
		for {
			m := make(map[string]interface{})
			err := c.ReadJSON(&m)
			// mt, m, err := c.ReadMessage()
			if err != nil {
				// 判断err类型是不是CloseGoingAway、CloseNormalClosure。
				// 如果是CloseGoingAway(客户端断线了)、CloseNormalClosure(正常关闭)，这些都是正常的事情，正常的停掉就可以了。
				// 否则都是不太正常的，它有可能是奇奇怪怪的close或者是数据格式错了等严重的错误。这里就把错误给打印出来。
				if !websocket.IsCloseError(err,
					websocket.CloseGoingAway,
					websocket.CloseNormalClosure) {
					fmt.Printf("unexpected read error: %v\n", err) // 不是我期待的错误。
				}
				done <- struct{}{} // 发一个空结构体`struct{}`表示的是结构体类型，第二个花括号是初始化这个结构体。
				break
			}
			fmt.Printf("message received: %v\n", m)
		}
	}()

	i := 0
	for {
		select {
		case <-time.After(200 * time.Millisecond): // 200毫秒之后就不做任何事情继续下一个循环
		case <-done: // 接收到done信号就退出该函数。handleWebSocket的整个过程就结束了
			return
		}

		i++
		err := c.WriteJSON(map[string]string{
			"hello":  "websocket",
			"msg_id": strconv.Itoa(i),
		})
		// 这个出错处理很有讲究，它在什么时候会写数据失败，这牵扯到如何干净的断开
		if err != nil {
			fmt.Printf("cannot write json: %v\n", err)
		}
	}
}
